<?php


namespace Hx;

defined('TIME_START') or define('TIME_START', microtime(true));

defined('MEMORY_START') or define('MEMORY_START', memory_get_usage());

/**
 * 核心程序类
 * Class App
 * @package Hx
 */
class App {

    //--------------

    /**
     * 应用名称
     * @var string
     */
    private static $name;

    /**
     * 应用目录
     * @var string
     */
    private static $path;

    /**
     * 设置应用信息
     * @param string $name
     * @param string $path
     */
    public static function setApp($name, $path) {
        self::$name = $name;
        self::$path = $path;
    }

    /**
     * 获取应用信息
     * @param string $key
     * @return null|string
     */
    public static function getApp($key) {
        if ($key === 'name') {
            return self::$name;
        }
        if ($key === 'path') {
            return self::$path;
        }
        return null;
    }

    //---------------

    /**
     * 配置数据
     * @var array
     */
    private static $configData = array();

    /**
     * 设置配置数据
     */
    public static function setConfig() {
        #添加数组配置
        if (func_num_args() == 1) {
            $arg = func_get_arg(0);
            if (is_array($arg)) {
                self::$configData += $arg;
            }
            return;
        }
        #key/value方式设置
        if (func_num_args() == 2) {
            self::$configData[func_get_arg(0)] = func_get_arg(1);
        }
    }

    /**
     * 获取配置数据
     * @param bool|string $key
     * @return array|string
     */
    public static function getConfig($key = true) {
        if ($key === true) {
            return self::$configData;
        }
        return self::$configData[$key];
    }

    //----------------------------

    /**
     * 已经加载的库类
     * @var array
     */
    private static $imported = array();

    /**
     * 判断是否已经加载
     * @param string $className
     * @return bool
     */
    public static function isImport($className) {
        return isset(self::$imported[$className]);
    }

    /**
     * 进入库类
     * @param string $className
     * @param bool $return
     * @return bool
     */
    public static function import($className, $return = false) {
        if (self::isImport($className)) {
            return true;
        }
        $file = '';
        if (strpos($className, 'Hx') === 0) { #处理Hx命名空间下的核心库类
            $file = str_replace(array('\\', 'Hx/'), array('/', __DIR__ . '/'), $className) . '.php';
        } elseif (strpos($className, self::$name) === 0) { #处理应用命名空间下的库类
            $file = str_replace(array('\\', self::$name . '/'), array('/', self::$path), $className) . '.php';
        }
        if (is_file($file)) {
            require_once $file;
            self::$imported[$className] = $file;
            return true;
        }
        if ($return) {
            return false;
        }
        self::error('无法加载类库：' . $className, 'CORE');
        return false;
    }

    //---------------------------

    /**
     * 已经实例化的Action
     * @var array
     */
    private static $actions = array();

    /**
     * 获取或调用Action方法
     * @param string $cmd
     * @param array $arg
     * @return mixed
     */
    public static function Action($cmd, $arg = array()) {
        #调用Action操作
        if (strstr($cmd, '->')) {
            $opt = explode('->', $cmd);
            $action = self::Action($opt[0]);
            if (!is_callable(array($action, $opt[1]))) {
                self::error('调用无效的方法：' . $cmd, 'CORE');
            }
            Event::trigger('app-action-' . $opt[0] . '-' . $opt[1]);
            return call_user_func_array(array($action, $opt[1]), $arg);
        } else { #获取Action实例
            if (!self::$actions[$cmd]) {
                $action = self::$name . '\\App\\Action\\' . $cmd;
                self::$actions[$cmd] = new $action();
            }
            return self::$actions[$cmd];
        }
    }

    //-----------------------------------------

    /**
     * 数据库连接对象
     * @var array
     */
    private static $dbs = array();

    /**
     * 获取数据库对象
     * @param string $name
     * @return Db
     */
    public static function Db($name = 'default') {
        if (!self::$dbs[$name]) {
            $cfg = self::$configData['database'][$name];
            $class = 'Hx\\Db\\Db' . $cfg['driver'];
            self::$dbs[$name] = new $class($cfg);
            Event::trigger('app-db-connect-' . $name);
        }
        return self::$dbs[$name];
    }

    /**
     * 创建一个SQL对象
     * @param string $table 表格
     * @param string $cols 字段
     * @return Sql
     */
    public static function Sql($table, $cols = '*') {
        $cols = explode(',', $cols);
        return new Sql($table, $cols);
    }

    //----------------------------------------------

    /**
     * 模型对象列表
     * @var array
     */
    private static $models = array();

    /**
     * 获取或调用Model
     * @param string $cmd
     * @param array $arg
     * @return mixed
     */
    public static function Model($cmd, $arg = array()) {
        if (strstr($cmd, '->')) {
            $opt = explode('->', $cmd);
            $model = self::Model($opt[0]);
            if (!is_callable(array($model, $opt[1]))) {
                self::error('调用无效的方法：' . $cmd, 'CORE');
            }
            Event::trigger('app-model-' . $opt[0] . '-' . $opt[1]);
            return call_user_func_array(array($model, $opt[1]), $arg);
        } else {
            if (!self::$models[$cmd]) {
                $model = self::$name . '\\App\Model\\' . $cmd;
                self::$models[$cmd] = new $model();
            }
            return self::$models[$cmd];
        }
    }


    //------------------------------

    /**
     * 错误信息处理
     * @param $message
     * @param $class
     * @param int $code
     * @param array $trace
     */
    public static function error($message, $class, $code = 500, $trace = array()) {
        $error = new Error($message, $class, $code, $trace);
        Event::trigger('error-' . $code, array($error));
        Response::setResBody($error->display());
        Response::setStatus($code);
        Response::sendResponse();
        exit;
    }

    //-----------------------------

    /**
     * 加载配置文件中的数据替换原数据
     */
    private static function loadConfigFile() {
        $fileConfig = array('database' => '数据库', 'cache' => '缓存', 'event' => '事件');
        #把原来的文件地址，替换为文件中的数据
        foreach ($fileConfig as $k => $v) {
            if (!self::$configData[$k]) {
                continue;
            }
            $file = self::$path . self::$configData[$k];
            if (!is_file($file)) {
                self::error($v . '配置文件丢失：' . self::$configData[$k], 'CORE');
            }
            self::$configData[$k] = require_once $file;
        }
        Event::trigger('app-load-config');
    }

    /**
     * 运行准备
     * 注册系统调用函数
     */
    private static function prepare() {
        #加载别名文件，简化调用函数
        if (self::$configData['alias']) {
            require_once 'alias.php';
        }
        #初始化请求内容解析
        Request::initRequest();
        #崩溃处理函数
        register_shutdown_function(function () {
            $error = error_get_last();
            if ($error['type'] <= 128 && $error['type'] != E_WARNING && $error['type'] != E_NOTICE) {
                App::error($error['message'], 'RUNTIME', 500, array($error));
            }
        });
        #异常处理函数
        set_exception_handler(function (\Exception $exc) {
            App::error($exc->getMessage(), strtoupper(get_class($exc)), 500, $exc->getTrace());
        });
        #注册事件
        if (self::$configData['event']) {
            Event::reg(self::$configData['event']);
        }
        Event::trigger('app-prepare');
    }

    /**
     * 运行核心库
     */
    public static function run() {
        #自动加载函数
        spl_autoload_register('Hx\App::import');
        #初始化应用
        self::loadConfigFile();
        self::prepare();
        #调用boot文件
        if (self::$configData['bootstrap']) {
            $file = self::$path . self::$configData['bootstrap'];
            if (!is_file($file)) {
                self::error('bootstrap文件丢失：' . self::$configData['bootstrap'], 'CORE');
            }
            require_once $file;
        }
        Event::trigger('app-router-dispatch');
        $result = Router::dispatch();
        #自动文本内容返回
        if (is_string($result)) {
            Response::setResBody($result);
        }
        Event::trigger('app-response', array($result));
        Response::sendResponse();
    }
}